﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace CityDashboard.Services
{

    /// <summary>
    /// Sending requests and receiving responses from server by a url 
    /// </summary>
    public class HttpService
    {
        readonly HttpClient _httpClient;
        private CancellationTokenSource _cts = new CancellationTokenSource();

        public HttpService()
        {
            var handler = new HttpClientHandler
            {
                AllowAutoRedirect = true,
                UseCookies = true,
                CookieContainer = new CookieContainer(),
            };

            //if (handler.SupportsAutomaticDecompression)
            //    handler.AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate;


            _httpClient = new HttpClient(handler);
            _httpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko");
            _httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "text/html, application/xhtml+xml, */*");
            _httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Language", "en-US");
        }
        public void CancelCurrentRequest()
        {
            if (_cts != null)
                _cts.Cancel();
        }

        public event EventHandler ConnectionFailed;

        /// <summary>
        /// Using GET method to send data and receiving response by a URL
        /// </summary>
        /// <param name="url">URL for get method</param>
        /// <param name="referrer">Referrer URL</param>
        /// <returns>response string</returns>
        public async Task<string> GetAsync(string url, string referrer = "")
        {
            try
            {
                //_cts = new CancellationTokenSource();

                //if (!NetworkInterface.GetIsNetworkAvailable())
                //    throw new NoInternetConnection();

                if (!string.IsNullOrEmpty(referrer))
                {
                    _httpClient.DefaultRequestHeaders.Referrer = new Uri(referrer);
                }

                _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/json"));

                var response = await _httpClient.GetAsync(url, _cts.Token);

                response.EnsureSuccessStatusCode();

                var content = await response.Content.ReadAsStringAsync();
                return content;
            }
            catch (Exception e)
            {
                if (ConnectionFailed != null)
                    ConnectionFailed(e, EventArgs.Empty);
                throw;
            }
        }

        public async Task<T> GetAsync<T>(string url)
        {
           return JsonConvert.DeserializeObject<T>(await GetAsync(url));
        }
        public async Task<string> PutAsync(string url, HttpContent content)
        {
            try
            {
                var response = await _httpClient.PutAsync(url, content);
                response.EnsureSuccessStatusCode();
                return await response.Content.ReadAsStringAsync();
            }
            catch (Exception e)
            {
                if (ConnectionFailed != null)
                    ConnectionFailed(e, EventArgs.Empty);
                throw;
            }
        }
        public async Task<string> GetAsync(string url, CancellationToken token, string referrer = "")
        {
            try
            {

                if (!NetworkInterface.GetIsNetworkAvailable())
                    throw new NoInternetConnection();

                if (!string.IsNullOrEmpty(referrer))
                {
                    _httpClient.DefaultRequestHeaders.Referrer = new Uri(referrer);
                }



                var response = await _httpClient.GetAsync(url, token);

                response.EnsureSuccessStatusCode();

                var content = await response.Content.ReadAsStringAsync();
                return content;
            }
            catch (Exception e)
            {
                if (ConnectionFailed != null)
                    ConnectionFailed(e, EventArgs.Empty);
                throw;
            }
        }
        /// <summary>
        /// Using POST method to send a string data and receiving response by a URL
        /// </summary>
        /// <param name="url">URL for POST</param>
        /// <param name="data">data</param>
        /// <param name="referrer">Referrer URL</param>
        /// <returns>response string</returns>
        public async Task<string> PostAsync(string url, string data, string referrer = "")
        {
            try
            {
                if (!NetworkInterface.GetIsNetworkAvailable())
                    throw new NoInternetConnection();

                if (!string.IsNullOrEmpty(referrer))
                {
                    _httpClient.DefaultRequestHeaders.Referrer = new Uri(referrer);
                }
                _cts = new CancellationTokenSource();
                var response = await _httpClient.PostAsync(url, new StringContent(data), _cts.Token);

                response.EnsureSuccessStatusCode();

                var content = await response.Content.ReadAsStringAsync();
                return content;
            }
            catch (Exception e)
            {
                if (ConnectionFailed != null)
                    ConnectionFailed(e, EventArgs.Empty);
                throw;
            }
        }

        // ReSharper disable once MethodOverloadWithOptionalParameter
        public async Task<string> PostAsync(string url, string data, string dataType, string referrer = "")
        {
            if (referrer == null) throw new ArgumentNullException("referrer");

            try
            {
                if (!NetworkInterface.GetIsNetworkAvailable())
                    throw new NoInternetConnection();

                if (!string.IsNullOrEmpty(referrer))
                {
                    _httpClient.DefaultRequestHeaders.Referrer = new Uri(referrer);
                }
                _cts = new CancellationTokenSource();
                var postData = new StringContent(data);
                postData.Headers.ContentType = new MediaTypeHeaderValue(dataType);
                var response = await _httpClient.PostAsync(url, postData, _cts.Token);

                response.EnsureSuccessStatusCode();

                var content = await response.Content.ReadAsStringAsync();
                return content;
            }
            catch (Exception e)
            {
                if (ConnectionFailed != null)
                    ConnectionFailed(e, EventArgs.Empty);
                throw;
            }
        }

        /// <summary>
        /// Using POST method to send a key,value data and receiving responses by a URL
        /// </summary>
        /// <param name="url">URL for POST</param>
        /// <param name="data">data</param>
        /// <param name="referrer">REferrer URL</param>
        /// <returns>responses string</returns>
        public async Task<string> PostAsync(string url, Dictionary<string, string> data, string referrer = "")
        {
            try
            {
                if (!NetworkInterface.GetIsNetworkAvailable())
                    throw new NoInternetConnection();
                if (!string.IsNullOrEmpty(referrer))
                {
                    _httpClient.DefaultRequestHeaders.Referrer = new Uri(referrer);
                }
                _cts = new CancellationTokenSource();
                var response = await _httpClient.PostAsync(url, new FormUrlEncodedContent(data), _cts.Token);

                response.EnsureSuccessStatusCode();

                var content = await response.Content.ReadAsStringAsync();
                return content;
            }
            catch (Exception e)
            {
                if (ConnectionFailed != null)
                    ConnectionFailed(e, EventArgs.Empty);
                throw;
            }
        }

        public Task<Stream> GetSteamAsync(string url)
        {
            return _httpClient.GetStreamAsync(url);
        }
        public Task<byte[]> GetByteArrayAsync(string url)
        {
            return _httpClient.GetByteArrayAsync(url);
        }
    }

    public class NoInternetConnection : Exception
    {
    }
}
